feat(payment): accept flexible single-node proof bundles#136
Merged
jacderida merged 3 commits intoJun 12, 2026
Merged
Conversation
747277b to
861f197
Compare
Verify paid single-node quotes from 1..=CLOSE_GROUP_SIZE supplied quotes while preserving the existing 0x01 proof format and 3x payment requirement. Run direct client PUTs and fresh chunk replication through the full ClientPut check set, including local close-group receiver membership. Run fresh paid-list admission through the same live payment checks with K-wide receiver membership, and keep verified-payment cache entries scoped by verification strength.
861f197 to
692a4a0
Compare
Move receiver placement checks out of PaymentVerifier so proof verification only validates payment contents and paid-quote issuer locality. Direct client PUTs now check local close-group storage responsibility before payment verification, while fresh replication and paid-list notification paths keep their pre-verification admission gates. Paid quote issuers are now checked against the configured close group instead of K closest peers.
Collaborator
|
Re-review after The paid-issuer-width concern from my earlier review looks addressed. What I verified:
Local checks run on this head:
CI at time of review: builds, clippy, format, docs, security audit, and no-logging test are green; OS matrix tests are still pending. No remaining blocker from my focused review, pending those matrix tests completing green. |
jacderida
approved these changes
Jun 12, 2026
grumbach
added a commit
to grumbach/ant-node
that referenced
this pull request
Jul 16, 2026
…ions ADR-0004 binds every quote's price to its issuer's committed key count — a price CEILING. Removing the old local floor (b52f457) left no revenue floor at all: the node verifier accepts 1..=7 quote proofs and fully validates only the median-priced candidate, so a modified client can fetch quotes from the whole neighbourhood, settle only the cheapest valid one 3x, and every receiver accepts. The flexible-bundle change (WithAutonomi#136) was safe while the old floor stood; their combination is the hole, and the one-quote client (ant-client WithAutonomi#150) makes that path routine. Add the floor half, priced from the SAME live commitment the receiver's own QuoteGenerator uses (committed responsible key count via a new non-mutating CommitmentSource::current_binding_snapshot — never current_chunks(), whose divergence from commitment counts is what false-rejected honest quotes under the pre-ADR-0004 floor; and never current_binding_for_quote(), which would extend answerability). Requirement on single-node store admissions: settled >= 3 x tolerance% x calculate_price(local committed key count) alongside the existing settled >= 3 x median check, i.e. an honest client may overpay a cheap quote to clear stricter receivers. No live commitment (fresh node, retired, restart window, unwired) prices the floor at baseline — vacuous, never an outage mode. Shadow-first rollout: default is observe-only with one telemetry line per evaluated admission (target ant_node::payment::price_floor) recording paid price, settled amount, local count/price, and the would-reject verdict. Enforcement is per-node via ANT_PRICE_FLOOR_ENFORCE=1 for canary rollout (tolerance override: ANT_PRICE_FLOOR_TOLERANCE_PERCENT, default 50); unsetting it is the kill switch. Clients need 4-of-20 stores, so canary enforcement cannot fail honest uploads while telemetry calibrates the tolerance. A floor rejection is an economic admission decision only, never trust evidence. Scope: split VerificationContext::FreshReplication out of ClientPut — verified identically everywhere (store-strength cache, cross-check, first-audit) so the floor's telemetry can distinguish direct ingress from fan-out and the fan-out policy stays tunable without touching direct PUTs. The floor applies to both (one cheap accepting node must not fan a below-floor proof around everyone else's floor) and NEVER to paid-list admission, repair, or cache hits — no historical receipt is repriced. Merkle proofs are untouched. Tests: cheapest-of-K settlement rejected under enforcement (direct PUT and fresh replication), exactly-at-floor overpayment accepted, shadow mode never rejects, no-commitment receivers stay baseline-vacuous, paid-list admission never repriced.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR changes node-side payment verification for the existing
0x01single-node proof format. It keeps the proof tag and wire serialization compatible, allows upgraded nodes to verify smaller quote bundles, and separates proof verification from receiver/admission placement checks.The payment verifier now validates proof contents plus paid-quote issuer locality. The call sites decide whether this node is allowed to accept the write before payment verification runs:
Added
0x01single-node proof verification for any1..=CLOSE_GROUP_SIZEquote bundle:0quotes are rejected.1quote is treated as the median-paid quote.2..=7quotes are sorted by price and the median-priced quote is treated as the paid quote.CLOSE_GROUP_SIZEquotes are rejected.SingleNodePayment::from_quotes, so partial bundles can be checked directly againstcompletedPayments(quote_hash).completedPayments(quote_hash)is at least3 * median_priceAntProtocolbefore payment verification/cache lookup.ClientPutentries satisfy later client PUT/fresh replication and paid-list lookups.PaidListAdmissionentries satisfy paid-list lookups only.PaymentVerifierConfig::close_group_size, wired fromReplicationConfigin production, devnet, and e2e test setup.AntProtocol::attach_p2p_node, so startup paths attach P2P through the protocol and keep direct PUT responsibility, paid-issuer, and merkle closeness checks wired consistently.Changed
PaymentVerifier::verify_paymentno longer checks whether this node is a receiver/member for the request.VerificationContextnow controls cache strength only.VerificationContext::ClientPutbecause the receiver immediately stores the chunk as part of fresh write fan-out.VerificationContext::PaidListAdmissionso it writes a paid-list-strength cache entry.AntProtocolrather than attaching only the verifier directly.Removed
VerificationContext::Replicationproof-verification mode.PaidListvsClientPutcache strength.CLOSE_GROUP_SIZErequirement for single-node proof bundles.Unchanged
0x01proofs remain valid.Verification
Ran locally:
cargo fmt --all -- --checkgit diff --checkcargo clippy --all-targets --all-features -- -D warningscargo test --lib --features test-utils -- --nocapture— 526 passedcargo test --test e2e --features test-utils data_types::chunk::tests::test_chunk_store_retrieve_small -- --nocapture— passed, local minimal testnetcargo test --test e2e --features test-utils replication::test_fresh_replication_propagates_to_close_group -- --nocapture— passed, local replication testnetcargo test --test e2e --features test-utils replication::scenario_24_fresh_replication_propagates_paid_notify -- --nocapture— passed, local replication testnetcargo test --test e2e --features test-utils payment_flow::helper_tests::test_init_testnet_and_evm -- --nocapture— passed, local 10-node payment-enabled testnet + Anvil setupcargo test --test e2e --features test-utils merkle_payment::test_attack_merkle_pay_yourself_fabricated_pool -- --nocapture— passed, local merkle/DHT attack testnetNote:
scripts/test_e2e.shwas not run from this checkout because it requirestarget/release/ant-cli, which is not built/present in this repository checkout.